import * as crypto from 'crypto';
import { promisify } from 'util';

const scrypt = promisify(crypto.scrypt);

// 加密数据的接口
interface EncryptedData {
  encrypted: string;
  iv: string;
  salt: string;
  authTag: string;
}

// 错误类型
class EncryptionError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'EncryptionError';
  }
}

class AESEncryption {
  private readonly algorithm = 'aes-256-gcm';

  constructor() {
    console.log('初始化 AES')
  }

  /**
   * 从密码生成密钥
   * @param password 密码
   * @param salt 盐值
   * @returns 生成的密钥
   */
  private async generateKeyFromPassword(password: string, salt: Buffer): Promise<Buffer> {
    try {
      const key = await scrypt(password, salt, 32) as Buffer;
      return key;
    } catch (error) {
      throw new EncryptionError(`密钥生成失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 加密文本
   * @param text 要加密的文本
   * @param password 密码
   * @returns 加密后的数据对象
   */
  async encrypt(text: string, password: string): Promise<EncryptedData> {
    try {
      const salt = crypto.randomBytes(16);
      const key = await this.generateKeyFromPassword(password, salt);
      const iv = crypto.randomBytes(12); // 对于 GCM，推荐 12 字节 IV

      const cipher = crypto.createCipheriv(this.algorithm, key, iv);

      let encrypted = cipher.update(text, 'utf8', 'hex');
      encrypted += cipher.final('hex');

      const authTag = cipher.getAuthTag();

      return {
        encrypted,
        iv: iv.toString('hex'),
        salt: salt.toString('hex'),
        authTag: authTag.toString('hex')
      };
    } catch (error) {
      throw new EncryptionError(`加密失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 解密文本
   * @param encryptedData 加密的数据对象
   * @param password 密码
   * @returns 解密后的原始文本
   */
  async decrypt(encryptedData: EncryptedData, password: string): Promise<string> {
    try {
      const key = await this.generateKeyFromPassword(
        password,
        Buffer.from(encryptedData.salt, 'hex')
      );

      const decipher = crypto.createDecipheriv(
        this.algorithm,
        key,
        Buffer.from(encryptedData.iv, 'hex')
      );

      decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));

      let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
      decrypted += decipher.final('utf8');

      return decrypted;
    } catch (error) {
      if (error instanceof Error && error.message.includes('bad decrypt')) {
        throw new EncryptionError('解密失败: 密码错误或数据已损坏');
      }
      throw new EncryptionError(`解密失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 验证密码是否正确（不实际解密，通过验证认证标签来检查）
   * @param encryptedData 加密的数据
   * @param password 密码
   * @returns 密码是否正确
   */
  async verifyPassword(encryptedData: EncryptedData, password: string): Promise<boolean> {
    try {
      // 尝试解密但不返回结果，只检查是否成功
      await this.decrypt(encryptedData, password);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * 生成随机密钥（用于直接存储）
   * @returns 十六进制格式的随机密钥
   */
  generateRandomKey(): string {
    return crypto.randomBytes(32).toString('hex');
  }

  /**
   * 使用预先生成的密钥进行加密
   * @param text 要加密的文本
   * @param keyHex 十六进制格式的密钥
   * @returns 加密后的数据对象
   */
  encryptWithKey(text: string, keyHex: string): EncryptedData {
    try {
      const key = Buffer.from(keyHex, 'hex');
      if (key.length !== 32) {
        throw new EncryptionError('密钥长度必须为32字节(256位)');
      }

      const iv = crypto.randomBytes(12);
      const cipher = crypto.createCipheriv(this.algorithm, key, iv);

      let encrypted = cipher.update(text, 'utf8', 'hex');
      encrypted += cipher.final('hex');

      const authTag = cipher.getAuthTag();

      return {
        encrypted,
        iv: iv.toString('hex'),
        salt: '', // 使用预生成密钥时不需要salt
        authTag: authTag.toString('hex')
      };
    } catch (error) {
      throw new EncryptionError(`使用密钥加密失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 使用预先生成的密钥进行解密
   * @param encryptedData 加密的数据对象
   * @param keyHex 十六进制格式的密钥
   * @returns 解密后的原始文本
   */
  decryptWithKey(encryptedData: EncryptedData, keyHex: string): string {
    try {
      const key = Buffer.from(keyHex, 'hex');
      if (key.length !== 32) {
        throw new EncryptionError('密钥长度必须为32字节(256位)');
      }

      const decipher = crypto.createDecipheriv(
        this.algorithm,
        key,
        Buffer.from(encryptedData.iv, 'hex')
      );

      decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));

      let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
      decrypted += decipher.final('utf8');

      return decrypted;
    } catch (error) {
      if (error instanceof Error && error.message.includes('bad decrypt')) {
        throw new EncryptionError('解密失败: 密钥错误或数据已损坏');
      }
      throw new EncryptionError(`使用密钥解密失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }
}

// 导出类型和类
export { AESEncryption, EncryptionError, type EncryptedData };
